﻿using TripleGame.Core;
using System;

namespace TripleGame.TestEngine
{
    public class TestEngine : ITripleGameEngine
    {
        private const char EMPTY_CELL = ' ';

        public string EngineName
        {
            get { return "TestEngine"; }
        }

        public void PerformMove(char[,] board, char playerLetter, 
            out int startX, out int startY, out int endX, out int endY)
        {
            int bestStartX = -1, bestStartY = -1, bestEndX = -1, bestEndY = -1;
            int minCells = 100;
            char[,] newBoard = new char[board.GetLength(0), board.GetLength(1)];
            for (int i = 3; i > 0; i--)
            {
                for (startY = 0; startY < board.GetLength(0); startY++)
                {
                    for (startX = 0; startX < board.GetLength(1); startX++)
                    {
                        bool availableMove;
                        availableMove = false;
                        availableMove = IsMoveValid(board, playerLetter, startY, startX, 1, 0, i);
                        if (availableMove)
                        {
                            for (int p = 0; p < board.GetLength(0); p++)
                            {
                                for (int j = 0; j < board.GetLength(1); j++)
                                {
                                    newBoard[p, j] = board[p, j];
                                }
                            }
                            for (int p = 0; p < 3; p++)
                            {
                                newBoard[startY, startX + p] = playerLetter;
                            }
                            int currFilled = MaxCells(newBoard, playerLetter);
                            if (currFilled < minCells)
                            {
                                minCells = currFilled;
                                bestStartX = startX;
                                bestStartY = startY;
                                bestEndX = bestStartX + 2;
                                bestEndY = bestStartY;
                            }
                        }
                        availableMove = IsMoveValid(board, playerLetter, startY, startX, 0, 1, i);
                        if (availableMove)
                        {
                            for (int p = 0; p < board.GetLength(0); p++)
                            {
                                for (int j = 0; j < board.GetLength(1); j++)
                                {
                                    newBoard[p, j] = board[p, j];
                                }
                            }
                            for (int p = 0; p < 3; p++)
                            {
                                newBoard[startY + p, startX] = playerLetter;
                            }
                            int currFilled = MaxCells(newBoard, playerLetter);
                            if (currFilled < minCells)
                            {
                                minCells = currFilled;
                                bestStartX = startX;
                                bestStartY = startY;
                                bestEndX = bestStartX;
                                bestEndY = bestStartY + 2;
                            }
                        }
                        availableMove = IsMoveValid(board, playerLetter, startY, startX, 1, 1, i);
                        if (availableMove)
                        {
                            for (int p = 0; p < board.GetLength(0); p++)
                            {
                                for (int j = 0; j < board.GetLength(1); j++)
                                {
                                    newBoard[p, j] = board[p, j];
                                }
                            }
                            for (int p = 0; p < 3; p++)
                            {
                                newBoard[startY + p, startX + p] = playerLetter;
                            }
                            int currFilled = MaxCells(newBoard, playerLetter);
                            if (currFilled < minCells)
                            {
                                minCells = currFilled;
                                bestStartX = startX;
                                bestStartY = startY;
                                bestEndX = bestStartX + 2;
                                bestEndY = bestStartY + 2;
                            }
                        }
                        availableMove = IsMoveValid(board, playerLetter, startY, startX, -1, -1, i);
                        if (availableMove)
                        {
                            for (int p = 0; p < board.GetLength(0); p++)
                            {
                                for (int j = 0; j < board.GetLength(1); j++)
                                {
                                    newBoard[p, j] = board[p, j];
                                }
                            }
                            for (int p = 0; p < 3; p++)
                            {
                                newBoard[startY - p, startX - p] = playerLetter;
                            }
                            int currFilled = MaxCells(newBoard, playerLetter);
                            if (currFilled < minCells)
                            {
                                minCells = currFilled;
                                bestStartX = startX;
                                bestStartY = startY;
                                bestEndX = bestStartX - 2;
                                bestEndY = bestStartY - 2;
                            }
                        }
                    }
                }
            }
            if (minCells != 100)
            {
                startX = bestStartX;
                startY = bestStartY;
                endX = bestEndX;
                endY = bestEndY;
                return;
            }

            throw new InvalidOperationException("No valid moves were found.");
        }

        private int MaxCells(char[,] board,char playerLetter)
        {
            char oLetter = 's';
            int allCells = 0;
            switch (playerLetter)
            {
                case 'a':
                case 'A':
                    oLetter = 'B';
                    break;
                case 'b':
                case 'B':
                    oLetter = 'A';
                    break;
            }
            for (int startY = 0; startY < board.GetLength(0); startY++)
            {
                for (int startX = 0; startX < board.GetLength(1); startX++)
                {
                    int cellsFilled = 0;
                    cellsFilled = CellsFilled(board, oLetter, startY, startX, 1, 0);
                    if (cellsFilled != 0)
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            board[startY, startX + i] = oLetter;
                        }
                        allCells += cellsFilled;
                    }
                    cellsFilled = CellsFilled(board, oLetter, startY, startX, 0, 1);
                    if (cellsFilled != 0)
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            board[startY + i, startX] = oLetter;
                        }
                        allCells += cellsFilled;
                    }
                    cellsFilled = CellsFilled(board, oLetter, startY, startX, 1, 1);
                    if (cellsFilled != 0)
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            board[startY + i, startX + i] = oLetter;
                        }
                        allCells += cellsFilled;
                    }
                    cellsFilled = CellsFilled(board, oLetter, startY, startX, -1, -1);
                    if (cellsFilled != 0)
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            board[startY - i, startX - i] = oLetter;
                        }
                        allCells += cellsFilled;
                    }
                }
            }
            return allCells;
        }

        private bool IsMoveValid(char[,] board, char playerLetter, 
            int startRow, int startCol, int stepX, int stepY, int max)
        {
            int row = startRow;
            int col = startCol;
            int numberOfEmptyCellsFilled = 0;
            for (int i = 1; i <= 3; i++)
            {
                if (row < 0 || row >= board.GetLength(0) ||
                    col < 0 || col >= board.GetLength(1))
                {
                    return false;
                }

                char currentCellUpcase = Char.ToUpperInvariant(board[row, col]);
                char playerLetterUpcase = Char.ToUpperInvariant(playerLetter);
                if (currentCellUpcase == EMPTY_CELL)
                {
                    numberOfEmptyCellsFilled++;
                }
                else if (currentCellUpcase != playerLetterUpcase)
                {
                    return false;
                }
                row += stepY;
                col += stepX;
            }

            if (numberOfEmptyCellsFilled != max)
            {
                return false;
            }

            return true;
        }

        private int CellsFilled(char[,] board, char playerLetter,
            int startRow, int startCol, int stepX, int stepY)
        {
            int row = startRow;
            int col = startCol;
            int numberOfEmptyCellsFilled = 0;
            for (int i = 1; i <= 3; i++)
            {
                if (row < 0 || row >= board.GetLength(0) ||
                    col < 0 || col >= board.GetLength(1))
                {
                    return 0;
                }

                char currentCellUpcase = Char.ToUpperInvariant(board[row, col]);
                char playerLetterUpcase = Char.ToUpperInvariant(playerLetter);
                if (currentCellUpcase == EMPTY_CELL)
                {
                    numberOfEmptyCellsFilled++;
                }
                else if (currentCellUpcase != playerLetterUpcase)
                {
                    return 0;
                }
                row += stepY;
                col += stepX;
            }

            if (numberOfEmptyCellsFilled == 0)
            {
                return 0;
            }

            return numberOfEmptyCellsFilled;
        }

        private bool SquareAvailable(char[,] board, char playerLetter, 
                                     out char dir, int startCol)
        {
            char playerLetterUpcase = Char.ToUpperInvariant(playerLetter);
            int x = startCol;
            if (board[2, x] == EMPTY_CELL || board[2, x] == playerLetterUpcase)
            {
                if (board[3, x] == EMPTY_CELL && board[4, x] == EMPTY_CELL)
                {
                    if ((x - 3 >= 0 && board[3, x - 3] == playerLetterUpcase && board[4, x - 3] == playerLetterUpcase) ||
                         x - 3 == -1)
                    {
                        if (board[3, x - 2] == EMPTY_CELL && board[3, x - 1] == EMPTY_CELL &&
                            board[4, x - 2] == EMPTY_CELL && board[4, x - 1] == EMPTY_CELL)
                        {
                            if (board[2, x - 1] == playerLetterUpcase && board[2, x - 2] == playerLetterUpcase)
                            {
                                dir = 'd';
                                return true;
                            }
                        }
                    }
                    if ((x + 3 < board.GetLength(1) && board[3, x + 3] == playerLetterUpcase && board[4, x + 3] == playerLetterUpcase) ||
                         x + 3 == board.GetLength(1))
                    {
                        if (board[3, x + 2] == EMPTY_CELL && board[3, x + 1] == EMPTY_CELL &&
                            board[4, x + 2] == EMPTY_CELL && board[4, x + 1] == EMPTY_CELL)
                        {
                            if (board[2, x + 1] == playerLetterUpcase && board[2, x + 2] == playerLetterUpcase)
                            {
                                dir = 'd';
                                return true;
                            }
                        }
                    }
                }
                else if (board[0, x] == EMPTY_CELL && board[1, x] == EMPTY_CELL)
                {
                    if ((x - 3 >= 0 && board[0, x - 3] == playerLetterUpcase && board[1, x - 3] == playerLetterUpcase) ||
                         x - 3 == -1)
                    {
                        if (board[0, x - 2] == EMPTY_CELL && board[0, x - 1] == EMPTY_CELL &&
                            board[1, x - 2] == EMPTY_CELL && board[1, x - 1] == EMPTY_CELL)
                        {
                            if (board[2, x - 1] == playerLetterUpcase && board[2, x - 2] == playerLetterUpcase)
                            {
                                dir = 'u';
                                return true;
                            }
                        }
                    }
                    if ((x + 3 < board.GetLength(1) && board[0, x + 3] == playerLetterUpcase && board[1, x + 3] == playerLetterUpcase) ||
                         x + 3 == board.GetLength(1))
                    {
                        if (board[0, x + 2] == EMPTY_CELL && board[0, x + 1] == EMPTY_CELL &&
                            board[1, x + 2] == EMPTY_CELL && board[1, x + 1] == EMPTY_CELL)
                        {
                            if (board[2, x + 1] == playerLetterUpcase && board[2, x + 2] == playerLetterUpcase)
                            {
                                dir = 'u';
                                return true;
                            }
                        }
                    }

                }
            }
            dir = 'x';
            return false;
        }
    }
}
